

#ifndef RTINETADDR_H
#define RTINETADDR_H

#include "RtDefines.h"
#include "RtStdCpp.h"

#ifndef RT_SUPPORT_ASYNC_RESOLVE_HOSTNAME
  #define RT_SUPPORT_ASYNC_RESOLVE_HOSTNAME 1
#endif // RT_SUPPORT_ASYNC_RESOLVE_HOSTNAME

/// The concept of <CRtInetAddr> is mainly copyed by <ACE_INET_Addr>
/// http://www.cs.wustl.edu/~schmidt/ACE.html
class RT_FW_EXPORT CRtInetAddr  
{
public:
	CRtInetAddr();
	
	/// Creates an <CRtInetAddr> from a <aPort> and the remote
	/// <aHostName>. The port number is assumed to be in host byte order.
	CRtInetAddr(LPCSTR aHostName, WORD aPort);

	/**
	* Initializes an <CRtInetAddr> from the <aIpAddrAndPort>, which can be
	* "ip-number:port-number" (e.g., "tango.cs.wustl.edu:1234" or
	* "128.252.166.57:1234").  If there is no ':' in the <address> it
	* is assumed to be a port number, with the IP address being
	* INADDR_ANY.
	*/
	CRtInetAddr(LPCSTR aIpAddrAndPort);

	RtResult Set(LPCSTR aHostName, WORD aPort);
	RtResult Set(LPCSTR aIpAddrAndPort);

	RtResult SetIpAddrByString(LPCSTR aIpAddr);
	RtResult SetIpAddrBy4Bytes(DWORD aIpAddr, BOOL aIsNetworkOrder = TRUE);
	RtResult SetPort(WORD aPort);

	/// Compare two addresses for equality.  The addresses are considered
	/// equal if they contain the same IP address and port number.
	bool operator == (const CRtInetAddr &aRight) const;

	/**
	 * Returns true if <this> is less than <aRight>.  In this context,
	 * "less than" is defined in terms of IP address and TCP port
	 * number.  This operator makes it possible to use <ACE_INET_Addr>s
	 * in STL maps.
	 */
	bool operator < (const CRtInetAddr &aRight) const;

	CRtString GetIpDisplayName() const;

	WORD GetPort() const { return ntohs(m_SockAddr.sin_port); }

	DWORD GetIpAddrIn4Bytes() const { return m_SockAddr.sin_addr.s_addr; }

	DWORD GetSize() const { return sizeof (sockaddr_in); }

	DWORD GetType() const { return m_SockAddr.sin_family; }

	const sockaddr_in* GetPtr() const 
	{ 
#ifdef RT_SUPPORT_ASYNC_RESOLVE_HOSTNAME
		RT_ASSERTE(IsResolved());
#endif // RT_SUPPORT_ASYNC_RESOLVE_HOSTNAME
		return &m_SockAddr; 
	}

	static BOOL IpAddrStringTo4Bytes(LPCSTR aIpStr, DWORD &aIpDword);
	static CRtString IpAddr4BytesToString(DWORD aIpDword);

#ifdef RT_SUPPORT_ASYNC_RESOLVE_HOSTNAME
	BOOL IsResolved() const
	{
		return m_strHostName.empty() ? TRUE : FALSE;
	}

	CRtString GetHostName() const { return m_strHostName; }

	RtResult TryResolve();
#endif // RT_SUPPORT_ASYNC_RESOLVE_HOSTNAME

public:
	static CRtInetAddr s_rtInetAddrNull;

private:
	sockaddr_in m_SockAddr;
	
#ifdef RT_SUPPORT_ASYNC_RESOLVE_HOSTNAME
	// m_strHostName is empty that indicates resovled successfully,
	// otherwise it needs resolving.
	CRtString m_strHostName;
#endif // RT_SUPPORT_ASYNC_RESOLVE_HOSTNAME
};


// inline functions
inline CRtInetAddr::CRtInetAddr()
{
	Set(NULL, 0);
}

inline CRtInetAddr::CRtInetAddr(LPCSTR aHostName, WORD aPort)
{
	Set(aHostName, aPort);
}

inline CRtInetAddr::CRtInetAddr(LPCSTR aIpAddrAndPort)
{
	Set(aIpAddrAndPort);
}

inline RtResult CRtInetAddr::SetPort(WORD aPort)
{
	m_SockAddr.sin_port = htons(aPort);
	return RT_OK;
}

inline CRtString CRtInetAddr::GetIpDisplayName() const
{
#ifdef RT_SUPPORT_ASYNC_RESOLVE_HOSTNAME
	if (!IsResolved())
		return m_strHostName;
#endif // RT_SUPPORT_ASYNC_RESOLVE_HOSTNAME

	return IpAddr4BytesToString(m_SockAddr.sin_addr.s_addr);
}

inline bool CRtInetAddr::operator == (const CRtInetAddr &aRight) const
{
#ifdef RT_SUPPORT_ASYNC_RESOLVE_HOSTNAME
	RT_ASSERTE(IsResolved());
#endif // RT_SUPPORT_ASYNC_RESOLVE_HOSTNAME
	
	// don't compare m_SockAddr.sin_zero due to getpeername() or getsockname() 
	// will fill it with non-zero value.
	return (::memcmp(
		&m_SockAddr, 
		&aRight.m_SockAddr, 
		sizeof(m_SockAddr) - sizeof(m_SockAddr.sin_zero)) == 0);
}

inline bool CRtInetAddr::operator < (const CRtInetAddr &aRight) const
{
#ifdef RT_SUPPORT_ASYNC_RESOLVE_HOSTNAME
	RT_ASSERTE(IsResolved());
#endif // RT_SUPPORT_ASYNC_RESOLVE_HOSTNAME

	return m_SockAddr.sin_addr.s_addr < aRight.m_SockAddr.sin_addr.s_addr
		|| (m_SockAddr.sin_addr.s_addr == aRight.m_SockAddr.sin_addr.s_addr 
		&& m_SockAddr.sin_port < aRight.m_SockAddr.sin_port);
}

inline RtResult CRtInetAddr::SetIpAddrBy4Bytes(DWORD aIpAddr, BOOL aIsNetworkOrder)
{
#ifdef RT_SUPPORT_ASYNC_RESOLVE_HOSTNAME
	// empty m_strHostName to indicate resovled successfully.
	m_strHostName.resize(0);
#endif // RT_SUPPORT_ASYNC_RESOLVE_HOSTNAME
	
	if (aIsNetworkOrder)
		m_SockAddr.sin_addr.s_addr = aIpAddr;
	else
		m_SockAddr.sin_addr.s_addr = htonl(aIpAddr);
	return RT_OK;
}

inline BOOL CRtInetAddr::IpAddrStringTo4Bytes(LPCSTR aIpStr, DWORD &aIpDword)
{
	aIpDword = INADDR_ANY;
	BOOL bAddrOk = TRUE;
	if (aIpStr && *aIpStr) {
#ifdef RT_WIN32
		aIpDword = ::inet_addr(aIpStr);
		if (aIpDword == INADDR_NONE && strcmp("255.255.255.255", aIpStr)) {
			bAddrOk = FALSE;
		}
#else
		bAddrOk = ::inet_pton(AF_INET, aIpStr, &aIpDword) > 0 ? TRUE : FALSE;
#endif // RT_WIN32
	}
	return bAddrOk;
}


#endif // !RTINETADDR_H
